<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta
      name="viewport"
      content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no"
    />
    <meta
      name="description"
      content="Demonstration of large numbers of clipping planes."
    />
    <meta name="cesium-sandcastle-labels" content="Development" />
    <title>Cesium Demo</title>
    <script type="text/javascript" src="../Sandcastle-header.js"></script>
    <script
      type="text/javascript"
      src="../../../Build/CesiumUnminified/Cesium.js"
      nomodule
    ></script>
    <script type="module" src="../load-cesium-es6.js"></script>
  </head>
  <body class="sandcastle-loading" data-sandcastle-bucket="bucket-requirejs.html">
    <style>
      @import url(../templates/bucket.css);
    </style>
    <div id="cesiumContainer" class="fullSize"></div>
    <div id="loadingOverlay"><h1>Loading...</h1></div>
    <div id="toolbar">
      <table>
        <tbody>
          <tr>
            <td>Distance</td>
            <td>
              <input
                type="range"
                min="-100.0"
                max="100.0"
                step="1.0"
                data-bind="value: cylinderRadius, valueUpdate: 'input'"
              />
              <input type="text" size="5" data-bind="value: cylinderRadius" />
            </td>
            <td>Plane Count</td>
            <td>
              <input
                type="range"
                min="1"
                max="128"
                step="1"
                data-bind="value: planeCount, valueUpdate: 'input'"
              />
              <input type="text" size="5" data-bind="value: planeCount" />
            </td>
          </tr>
        </tbody>
      </table>
      <select data-bind="options: exampleTypes, value: currentExampleType"></select>
    </div>
    <script id="cesium_sandcastle_script">
      window.startup = async function (Cesium) {
        "use strict";
        //Sandcastle_Begin
        const viewer = new Cesium.Viewer("cesiumContainer", {
          infoBox: false,
          selectionIndicator: false,
          shouldAnimate: true,
          projectionPicker: true,
          terrain: Cesium.Terrain.fromWorldTerrain(),
        });

        viewer.extend(Cesium.viewerCesium3DTilesInspectorMixin);

        const globe = viewer.scene.globe;
        globe.depthTestAgainstTerrain = true;

        let cylinderRadius = -20.0;
        let radiusMultiplier = 1.0;

        let steps = 32;
        let clippingPlanes = [];
        let modelEntityClippingPlanes;
        let clippingModeUnion = false;
        let enabled = true;

        const clipObjects = ["model", "b3dm", "pnts", "i3dm", "terrain"];
        const viewModel = {
          cylinderRadius: cylinderRadius,
          exampleTypes: clipObjects,
          currentExampleType: clipObjects[0],
          planeCount: steps,
        };

        Cesium.knockout.track(viewModel);

        const toolbar = document.getElementById("toolbar");
        Cesium.knockout.applyBindings(viewModel, toolbar);

        Cesium.knockout
          .getObservable(viewModel, "cylinderRadius")
          .subscribe(function (newValue) {
            cylinderRadius = parseFloat(viewModel.cylinderRadius);
            updatePlanes();
          });

        Cesium.knockout
          .getObservable(viewModel, "planeCount")
          .subscribe(function (newValue) {
            const newSteps = parseFloat(viewModel.planeCount);
            if (newSteps !== steps) {
              steps = newSteps;
              modelEntityClippingPlanes.removeAll();
              computePlanes();
            }
          });

        const scene = viewer.scene;
        const planeEntities = [];
        let selectedPlane;

        function updatePlanes() {
          for (let i = 0; i < clippingPlanes.length; i++) {
            const plane = clippingPlanes[i];
            plane.distance = cylinderRadius * radiusMultiplier;
          }
        }

        function computePlanes() {
          const stepDegrees = Cesium.Math.TWO_PI / steps;
          clippingPlanes = [];

          for (let i = 0; i < steps; i++) {
            const angle = i * stepDegrees;
            const dir = new Cesium.Cartesian3();
            dir.x = 1.0;
            dir.y = Math.tan(angle);
            if (angle > Cesium.Math.PI_OVER_TWO) {
              dir.x = -1.0;
              dir.y *= -1.0;
            }
            if (angle > Cesium.Math.PI) {
              dir.x = -1.0;
            }
            if (angle > Cesium.Math.PI_OVER_TWO * 3) {
              dir.x = 1.0;
              dir.y = -dir.y;
            }
            Cesium.Cartesian3.normalize(dir, dir);
            const newPlane = new Cesium.ClippingPlane(
              dir,
              cylinderRadius * radiusMultiplier,
            );
            modelEntityClippingPlanes.add(newPlane);
            clippingPlanes.push(newPlane);
          }
        }

        function createClippingPlanes(modelMatrix) {
          modelEntityClippingPlanes = new Cesium.ClippingPlaneCollection({
            modelMatrix: Cesium.defined(modelMatrix)
              ? modelMatrix
              : Cesium.Matrix4.IDENTITY,
            edgeWidth: 2.0,
            edgeColor: Cesium.Color.WHITE,
            unionClippingRegions: clippingModeUnion,
            enabled: enabled,
          });
          computePlanes();
        }

        function updateClippingPlanes() {
          return modelEntityClippingPlanes;
        }

        const modelUrl = "../../SampleData/models/CesiumAir/Cesium_Air.glb";
        const agiHqUrl = await Cesium.IonResource.fromAssetId(40866);
        const instancedUrl =
          "../../SampleData/Cesium3DTiles/Instanced/InstancedOrientation/tileset.json";
        const pointCloudUrl = await Cesium.IonResource.fromAssetId(5713);

        function loadModel(url) {
          createClippingPlanes();
          const position = Cesium.Cartesian3.fromDegrees(-123.0744619, 44.0503706, 300.0);
          const heading = 0.0;
          const pitch = 0.0;
          const roll = 0.0;
          const hpr = new Cesium.HeadingPitchRoll(heading, pitch, roll);
          const orientation = Cesium.Transforms.headingPitchRollQuaternion(position, hpr);
          const entity = viewer.entities.add({
            name: url,
            position: position,
            orientation: orientation,
            model: {
              uri: url,
              scale: 20,
              minimumPixelSize: 100.0,
              clippingPlanes: new Cesium.CallbackProperty(updateClippingPlanes, false),
            },
          });
          viewer.trackedEntity = entity;
        }

        let tileset;
        let currentUrl;
        async function loadTileset(url, height) {
          currentUrl = url;
          createClippingPlanes();
          tileset = await Cesium.Cesium3DTileset.fromUrl(url, {
            clippingPlanes: modelEntityClippingPlanes,
            enableDebugWireframe: true,
          });
          if (currentUrl !== url) {
            // Another scenario has been loaded. Discard the result.
            return;
          }
          viewer.scene.primitives.add(tileset);

          const boundingSphere = tileset.boundingSphere;

          const cartographic = Cesium.Cartographic.fromCartesian(boundingSphere.center);
          const surface = Cesium.Cartesian3.fromRadians(
            cartographic.longitude,
            cartographic.latitude,
            0.0,
          );
          const offset = Cesium.Cartesian3.fromRadians(
            cartographic.longitude,
            cartographic.latitude,
            height,
          );
          const translation = Cesium.Cartesian3.subtract(
            offset,
            surface,
            new Cesium.Cartesian3(),
          );
          tileset.modelMatrix = Cesium.Matrix4.fromTranslation(translation);

          const radius = boundingSphere.radius;
          viewer.camera.viewBoundingSphere(
            boundingSphere,
            new Cesium.HeadingPitchRange(0.5, -0.2, radius * 4.0),
          );
          viewer.camera.lookAtTransform(Cesium.Matrix4.IDENTITY);
        }

        loadModel(modelUrl);

        Cesium.knockout
          .getObservable(viewModel, "currentExampleType")
          .subscribe(function (newValue) {
            reset();

            if (newValue === clipObjects[0]) {
              // Model
              loadModel(modelUrl);
            } else if (newValue === clipObjects[1]) {
              // B3dm photogrammetry
              return loadTileset(agiHqUrl, 0.0);
            } else if (newValue === clipObjects[2]) {
              // Point clouds
              radiusMultiplier = 20.0;
              return loadTileset(pointCloudUrl, 0.0).then(function () {
                tileset.pointCloudShading.attenuation = true;
              });
            } else if (newValue === clipObjects[3]) {
              // i3dm
              loadTileset(instancedUrl, 100.0);
            } else if (newValue === clipObjects[4]) {
              // Terrain
              const position = Cesium.Cartesian3.fromRadians(
                // eslint-disable-next-line no-loss-of-precision
                -2.0872979473351286,
                0.6596620013036164,
                2380.0,
              );
              const entity = viewer.entities.add({
                position: position,
                model: {
                  uri: "../../SampleData/models/CesiumMan/Cesium_Man.glb",
                  minimumPixelSize: 128,
                  scale: 40,
                },
              });
              viewer.trackedEntity = entity;
              createClippingPlanes(entity.computeModelMatrix(Cesium.JulianDate.now()));
              globe.clippingPlanes = modelEntityClippingPlanes;
            }
            updatePlanes();
          });

        function reset() {
          radiusMultiplier = 1.0;
          viewModel.cylinderRadius = cylinderRadius;
          viewer.entities.removeAll();
          viewer.scene.primitives.removeAll();
          globe.clippingPlanes = undefined; // destroy Globe clipping planes, if any
          modelEntityClippingPlanes = undefined;
        }

        Sandcastle.addToggleButton("union", clippingModeUnion, function (checked) {
          clippingModeUnion = checked;
          modelEntityClippingPlanes.unionClippingRegions = clippingModeUnion;
        });

        Sandcastle.addToggleButton("enabled", enabled, function (checked) {
          enabled = checked;
          modelEntityClippingPlanes.enabled = enabled;
        }); //Sandcastle_End
      };
      if (typeof Cesium !== "undefined") {
        window.startupCalled = true;
        window.startup(Cesium).catch((error) => {
          "use strict";
          console.error(error);
        });
        Sandcastle.finishedLoading();
      }
    </script>
  </body>
</html>
